package com.openvid.webrtc.openvid.decoder;


import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * @author jinyf
 */
public class VideoDecoderThread extends Thread {

    private static final String TAG = "VideoThread";
    private static final long TIMEOUT_US = 10_000;

    private MediaExtractor mExtractor;
    private MediaCodec mDecoder;
    private boolean isEndOfStream = false;
    private long mStartTimeMs = 0;


    private File mSourceFile;
    private IVideoData mDataCallback;

    private int mWidth=0;
    private int mHeight=0;

    public VideoDecoderThread(File sourceFile, IVideoData callback) {
        mSourceFile = sourceFile;
        mDataCallback = callback;
    }

    private VideoSpeedController mSpeedControl=new VideoSpeedController();

    @Override
    public void run() {
        if (!mSourceFile.exists()) {
            return;
        }
        mExtractor = new MediaExtractor();
        try {
            mExtractor.setDataSource(mSourceFile.getPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < mExtractor.getTrackCount(); i++) {
            MediaFormat outPutFormat = mExtractor.getTrackFormat(i);
            String mime = outPutFormat.getString(MediaFormat.KEY_MIME);

            if (mime.startsWith("video/")) {
                mWidth=outPutFormat.getInteger(MediaFormat.KEY_WIDTH);
                mHeight=outPutFormat.getInteger(MediaFormat.KEY_HEIGHT);
                int frameRate=outPutFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
                mSpeedControl.setFixedPlaybackRate(frameRate);

                mExtractor.selectTrack(i);
                try {
                    mDecoder = MediaCodec.createDecoderByType(mime);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                outPutFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
                mDecoder.configure(outPutFormat, null, null, 0);
                mDecoder.start();
            }else{
                Log.i("123123","mime:"+mime);
            }
        }

        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        while (!isInterrupted()) {
            if (!isEndOfStream) {
                int inIndex = mDecoder.dequeueInputBuffer(TIMEOUT_US);
                if (inIndex >= 0) {
                    ByteBuffer[] inputBuffers = mDecoder.getInputBuffers();
                    ByteBuffer buffer = inputBuffers[inIndex];
                    int sampleSize = mExtractor.readSampleData(buffer, 0);
                    if (sampleSize < 0) {
//                        mExtractor.seekTo(0,MediaExtractor.SEEK_TO_CLOSEST_SYNC);
//                        continue;
                        //TODO
                        mDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        isEndOfStream = true;
                    } else {
                        mDecoder.queueInputBuffer(inIndex, 0, sampleSize, mExtractor.getSampleTime(), 0);
                        mExtractor.advance();
                    }
                }
            }
            int outIndex = mDecoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
            switch (outIndex) {
                case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                    //                    outputBuffers = mDecoder.getOutputBuffers();
                    break;
                case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                    break;
                case MediaCodec.INFO_TRY_AGAIN_LATER:
                    break;
                default:
                    ByteBuffer outputInfo = mDecoder.getOutputBuffer(outIndex);
                    if (outputInfo != null) {
                        outputInfo.position(0);
                        outputInfo.limit(bufferInfo.offset + bufferInfo.size);
                        //TODO
//                        byte[] yuv = new byte[outputInfo.remaining()];
//                        outputInfo.get(yuv);
                        try {
                            mSpeedControl.renderBlock(bufferInfo.presentationTimeUs);
                        } catch (InterruptedException e) {
                            break;
                        }
                        if (mDataCallback!=null&&!isInterrupted()){
//                            Log.i("223223","pts:"+bufferInfo.presentationTimeUs+"--st:"+mExtractor.getSampleTime());
                            mDataCallback.onVideoFrame(outputInfo,mWidth,mHeight, bufferInfo.presentationTimeUs);
                        }

                    }
                    mDecoder.releaseOutputBuffer(outIndex, true);
                    break;
            }
            if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                break;
            }
        }
        mDecoder.stop();
        mDecoder.release();
        mExtractor.release();

        if (mDataCallback!=null){
            mDataCallback.onVideoEnd(mSourceFile);
        }
    }

    public void seekTo(long time) {
        if (mExtractor != null) {
            mExtractor.seekTo(time, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
        }
    }

    public interface IVideoData {
        void onVideoFrame(ByteBuffer buffer,int w,int h, long ts);
        void onVideoEnd(File sourceFile);
    }

}